package com.pomelo.tool.service;

import com.pomelo.base.core.bean.BizException;
import com.pomelo.base.core.util.CommonUtil;
import com.pomelo.base.core.util.DbUtil;
import com.pomelo.base.core.util.ShellUtil;
import com.pomelo.base.core.util.StringUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @ClassName: DeployServiceImpl
 * @Description: 部署服务
 * @Author: sunxin
 * @Date: 2021/10/14 15:13
 **/
@Service public class DeployService {

    /**
     * 默认的数据库连接
     */
    @Value("${pomelo.default.database.url:jdbc:oracle:thin:@172.16.7.200:1521:ZHFWDB}") private String databaseUrl;

    /**
     * 默认的数据库驱动
     */
    @Value("${pomelo.default.driver:oracle.jdbc.driver.OracleDriver}") private String databaseDriver;

    /**
     * 默认的新建的用户的密码
     */
    @Value("${pomelo.default.pwd:wisedu}") private String DEFAULT_PASSWD;

    /**
     * 默认的数据库连接
     */
    @Value("${dev.database.url:172.16.5.120/ccs}") private String devDatabaseUrl;

    /**
     * 默认的目标数据库连接
     */
    @Value("${dev.database.url:172.16.5.54/urpdb}") private String tarDatabaseUrl;

    /**
     * 默认的数据库连接
     */
    @Value("${dev.database:jdbc:oracle:thin:@172.16.5.120:1521:ccs}") private String devDatabase;

    /**
     * 应用部署目录
     */
    public static final String DEPLOY_PATH_PREFIX = "/opt/wiseduAppGroups/";

    protected Log logger = LogFactory.getLog(this.getClass());

    @Autowired private GenGradleBuildService ggbSV;

    /**
     * svn
     */
    @Autowired private SvnService svnService;

    /**
     * @Description: 初始化tomcat
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:38
     **/
    public void initTomcat(String schoolCode, String pedestal, Map<String, Object> data) throws Exception {
        String shellFilePath = "shell/tomcat/copy_server.sh";
        logger.info("初始化tomcat");
        String cmd = generateCmd(shellFilePath, DEFAULT_PASSWD + " " + schoolCode + " " + pedestal);
        int code = ShellUtil.execCmd(cmd);
        if (code != 0) {
            throw new BizException("初始化Tomcat失败");
        }
        BufferedWriter out = null;
        try {
            out = new BufferedWriter(new FileWriter("/home/" + schoolCode + "/servers/server.out"));
            out.write("tail -f /home/" + schoolCode + "/servers/tomcat/logs/catalina.out ");
        } finally {
            IOUtils.closeQuietly(out);
        }
        logger.info("处理tomcat数据源连接文件");
        handleDataSourceXml(schoolCode, data);
    }

    /**
     * @Description: 根据传递的底座信息，复制公共app
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:40
     **/
    public void initCommonApp(String schoolCode, String pedestal) throws Exception {
        String shellFilePath = "shell/tomcat/copy_common_app.sh";
        String cmd = generateCmd(shellFilePath, " " + schoolCode + " " + pedestal);
        logger.info("复制底座公共应用");
        int code = ShellUtil.execCmd(cmd);
        if (code != 0) {
            throw new BizException("初始化底座应用失败");
        }
    }

    /**
     * @Description: 生成命令行
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:41
     **/
    public String generateCmd(String shellFilePath, String args) {
        File shellFile = new File(shellFilePath);
        if (!shellFile.exists()) {
            logger.error("脚本:" + shellFile.getAbsolutePath() + " 不存在");
            return null;
        }
        String cmd = "/bin/bash " + shellFile.getAbsolutePath();
        if (args != null) {
            cmd += " " + args;
        }
        return cmd;
    }

    /**
     * @Description: 处理数据源连接文件
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:41
     **/
    private void handleDataSourceXml(String schoolCode, Map<String, Object> data) throws Exception {
        File file = new File("shell/tomcat/demo.xml");
        if (!file.exists()) {
            throw new BizException("数据源文件不存在，请联系管理员");
        }
        // 创建SAXReader对象
        SAXReader sr = new SAXReader();
        // 关联xml
        Document document = sr.read(file);
        // 获取根元素
        Element context = document.getRootElement();
        // 利用context标签添加属性
        logger.info(context);
        context.addAttribute("path", "/" + schoolCode);
        context.addAttribute("docBase", schoolCode);

        String userId = "usr_" + schoolCode;
        Element resource = context.element("Resource");
        resource.addAttribute("username", userId);
        resource.addAttribute("password", "wisedu");
        resource.addAttribute("url", databaseUrl);
        File destDir = new File("/home/" + schoolCode + "/servers/tomcat/conf/Catalina/localhost");
        if (!destDir.exists()) {
            throw new BizException("服务器部署目录不存在，请联系管理员");
        }
        saveDocument(document, new File(destDir, schoolCode + ".xml"));

    }

    /**
     * @Description: 将新生成的数据源文件保存到指定目录
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:42
     **/
    private void saveDocument(Document document, File xmlFile) throws IOException {
        // 创建输出流
        Writer osWrite = null;
        XMLWriter writer = null;
        try {
            osWrite = new OutputStreamWriter(new FileOutputStream(xmlFile));
            // 获取输出的指定格式
            OutputFormat format = OutputFormat.createPrettyPrint();
            // 设置编码 ，确保解析的xml为UTF-8格式
            format.setEncoding("UTF-8");
            writer = new XMLWriter(osWrite, format);
            writer.write(document);
            writer.flush();
            writer.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            IOUtils.closeQuietly(osWrite);
        }

    }

    /**
     * @Description: 初始化执行标准
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:45
     **/
    public void initZxbz(String schoolCode) throws Exception {
        logger.info("初始化执行标准数据");
        String sql = "SELECT 'CREATE TABLE '||'usr_" + schoolCode
            + ".'||TABLE_NAME||' AS SELECT * FROM '||TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME LIKE 'T_ZXBZ_%'";
        List<Object> sqls = DbUtil.queryOnes(sql, null);
        if (CommonUtil.isEmptyList(sqls)) {
            return;
        }
        for (Object obj : sqls) {
            sql = StringUtil.toStr(obj);
            DbUtil.execute(sql, null);
        }
    }

    /**
     * @Description: 启动服务
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:45
     **/
    public void startUp(Map<String, Object> data) throws Exception {
        String schoolCode = StringUtil.toStr(data.get("SCHOOL_ID_"));
        String number = StringUtil.toStr(data.get("SERIAL_NUMBER_"));
        schoolCode = "u" + schoolCode + number;
        String startPath = "shell/tomcat/startServer.sh";
        String cmd = generateCmd(startPath, schoolCode);
        int code = ShellUtil.execCmd(cmd);
        if (code != 0) {
            throw new BizException("启动服务器失败");
        }
        data.put("STATE_", "1");
        DbUtil.saveOrUpdate("T_HRMS_PROJECT", data);
    }

    /**
     * @Description: 停止服务
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/20 9:29
     **/
    public void shutdown(Map<String, Object> data) throws Exception {
        String schoolCode = StringUtil.toStr(data.get("SCHOOL_ID_"));
        String number = StringUtil.toStr(data.get("SERIAL_NUMBER_"));
        schoolCode = "u" + schoolCode + number;
        String startPath = "shell/tomcat/stopServer.sh";
        String cmd = generateCmd(startPath, schoolCode);
        int code = ShellUtil.execCmd(cmd);
        if (code != 0) {
            throw new BizException("停止服务器失败");
        }
        data.put("STATE_", "0");
        DbUtil.saveOrUpdate("T_HRMS_PROJECT", data);
    }

    /**
     * @Description: 初始化数据库用户
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/16 9:46
     **/
    public void initDbUser(String userId) throws Exception {
        DbUtil.execute("create user " + userId
            + " identified by wisedu default tablespace TS_RS_D temporary tablespace TEMP profile DEFAULT");
        DbUtil.execute("grant connect,resource to " + userId);
        DbUtil.execute("grant unlimited tablespace to " + userId);
        DbUtil.execute("grant create table , create view to " + userId);
        DbUtil.execute("grant dba to " + userId);
        DbUtil.execute("grant select any table to " + userId);
    }

    /**
     * @Description: 保存项目
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/10/19 18:15
     **/
    public void saveProject(Map<String, Object> formData) throws Exception {
        String schoolCode = StringUtil.toStr(formData.get("SCHOOL_ID_"));
        String sql = "SELECT NVL(MAX(SERIAL_NUMBER_),0) FROM T_HRMS_PROJECT WHERE SCHOOL_ID_=?";
        int number = DbUtil.queryParseInt(sql, new Object[] {schoolCode});
        formData.put("SERIAL_NUMBER_", number + 1);
        String userId = "usr_" + schoolCode + (number + 1);
        formData.put("DATABASE_USERNAME_", userId);
        formData.put("DATABASE_PASSWORD_", "wisedu");
        formData.put("DATABASE_URL_", databaseUrl);
        formData.put("DATABASE_DRIVERCLASSNAME_", databaseDriver);
        formData.put("STATE_", "0");
        DbUtil.saveOrUpdate("T_HRMS_PROJECT", formData);
        deploy(formData);
    }

    /**
     * @Description: 部署
     * @Author: sunxin
     * @Param: 部署方案信息
     * @Return:
     * @Date: 2021/10/16 9:35
     **/
    private void deploy(Map<String, Object> data) throws Exception {
        String schoolCode = StringUtil.toStr(data.get("SCHOOL_ID_"));
        String pedestal = StringUtil.toStr(data.get("PEDESTAL_VERSION_"));
        String number = StringUtil.toStr(data.get("SERIAL_NUMBER_"));
        schoolCode = "u" + schoolCode + number;
        initTomcat(schoolCode, pedestal, data);
        initDbUser("usr_" + schoolCode);
        String copyDev = StringUtil.toStr(data.get("IS_COPY_DEV_"));
        //        if ("1".equals(copyDev)) {
        //            expImpDmp(schoolCode, data);
        //        } else {
        //            initZxbz(schoolCode);
        //        }
        initZxbz(schoolCode);
        File file = new File(DEPLOY_PATH_PREFIX, schoolCode);
        String username = StringUtil.toStr(data.get("SVN_USERNAME_"));
        String password = StringUtil.toStr(data.get("SVN_PASSWORD_"));
        String sourceSvnPath = StringUtil.toStr(data.get("SVN_PATH_"));
        String checkOutDir = file.getPath();
        if (!file.exists()) {
            file.mkdirs();
            svnService.checkOut(username, password, sourceSvnPath, checkOutDir);
        } else {
            svnService.update(username, password, sourceSvnPath, checkOutDir);
        }
        initCommonApp(schoolCode, pedestal);
        File workspace = new File(DEPLOY_PATH_PREFIX + schoolCode);
        File[] appDirs = workspace.listFiles();
        genAppDependFile(appDirs);
        genAppGradleFile(appDirs, schoolCode);
    }

    /**
     * 重新拉取应用进行部署
     *
     * @param data
     * @throws Exception
     */
    public void reDeploy(Map<String, Object> data) throws Exception {
        String schoolCode = StringUtil.toStr(data.get("SCHOOL_ID_"));
        String pedestal = StringUtil.toStr(data.get("PEDESTAL_VERSION_"));
        String number = StringUtil.toStr(data.get("SERIAL_NUMBER_"));
        schoolCode = "u" + schoolCode + number;

        File file = new File(DEPLOY_PATH_PREFIX, schoolCode);
        String username = StringUtil.toStr(data.get("SVN_USERNAME_"));
        String password = StringUtil.toStr(data.get("SVN_PASSWORD_"));
        String sourceSvnPath = StringUtil.toStr(data.get("SVN_PATH_"));
        String checkOutDir = file.getPath();
        if (!file.exists()) {
            throw new BizException("未检测到应用部署目录，请联系管理员");
        }
        svnService.update(username, password, sourceSvnPath, checkOutDir);
        File workspace = new File(DEPLOY_PATH_PREFIX + schoolCode);
        //清除拉取下拉的应用的classes文件
        clearClasses(workspace);
        initCommonApp(schoolCode, pedestal);
        File[] appDirs = workspace.listFiles();
        genAppDependFile(appDirs);
        genAppGradleFile(appDirs, schoolCode);
    }

    private void clearClasses(File workspace) throws Exception {
        File[] appDirs = workspace.listFiles();
        for (File appDir : appDirs) {
            if (!appDir.isDirectory()) {
                continue;
            }
            File src = new File(appDir, "src");
            File classes = new File(appDir, "classes");
            if (src.exists() && classes.exists()) {
                logger.info("删除应用" + appDir.getName() + "classes");
                deleteDir(classes);
            }
            File pubsrc = new File(appDir, "pub_src");
            File pubclasses = new File(appDir, "pub_classes");
            if (pubsrc.exists() && pubclasses.exists()) {
                logger.info("删除应用" + appDir.getName() + "pub_classes");
                deleteDir(pubclasses);
            }
        }
    }

    /**
     * @Description: 递归删除目录下的所有文件及子目录下所有文件
     * @Author: sunxin
     * @Param: 需要删除的目录
     * @Return:
     * @Date: 2021/10/21 13:40
     **/
    private static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空，可以删除
        return dir.delete();
    }

    /**
     * 生成应用的gradle文件，并进行编译
     *
     * @param appDirs    工作空间目录
     * @param schoolCode 学校代码
     * @throws Exception
     */
    private void genAppGradleFile(File[] appDirs, String schoolCode) throws Exception {
        List<String> appNames = new ArrayList<>();
        for (File appDir : appDirs) {
            if (!appDir.isDirectory()) {
                continue;
            }
            File appInfo = new File(appDir, "app_info.xml");

            if (!appInfo.exists()) {
                continue;
            }
            File classPath = new File(appDir, ".classpath");

            if (!classPath.exists()) {
                continue;
            }
            String name = appDir.getName();

            boolean gen = ggbSV.genBuildFile(schoolCode, name);
            if (gen) {
                appNames.add(name);
            }
        }
        for (String appName : appNames) {
            logger.info("构建应用：" + appName);
            String cmd = generateCmd("shell/tomcat/build_app.sh", schoolCode + " " + appName);
            int code = ShellUtil.execCmd(cmd);
            if (code != 0) {
                throw new BizException("构建应用" + appName + "失败");
            }
        }
    }

    /**
     * @Description: 生成db依赖文件
     * @Author: sunxin
     * @Param:
     * @Return:
     * @Date: 2021/11/4 14:28
     **/
    private void genAppDependFile(File[] appDirs) throws Exception {
        for (File appDir : appDirs) {
            if (!appDir.isDirectory()) {
                continue;
            }
            String name = appDir.getName();
            logger.info("检查应用" + name + "是否需要添加执行脚本依赖");
            if (!name.endsWith("$A")) {
                continue;
            }
            logger.info("应用" + name + "为定制应用，检查脚本依赖");
            File initDir = new File(appDir, "src/db/init$A");
            if (!initDir.exists()) {
                logger.info("应用" + name + "无脚本");
                continue;
            }
            File dependent = new File(initDir, "dependent.txt");

            if (dependent.exists()) {
                logger.info("应用" + name + "已存在脚本依赖");
                continue;
            }
            dependent.createNewFile();
            logger.info("应用" + name + "生成依赖脚本文件");
            String content = "unit:init";
            logger.info("应用" + name + "生成依赖脚本文件，内容：" + content);
            FileWriter fw = null;
            BufferedWriter bw = null;
            try {
                fw = new FileWriter(dependent);
                bw = new BufferedWriter(fw);
                bw.write(content);
            } catch (IOException e) {

            } finally {
                IOUtils.closeQuietly(bw);
                IOUtils.closeQuietly(fw);
            }

        }

    }

    public void expImpDmp(String schoolCode, Map<String, Object> data) {
        String username = StringUtil.toStr(data.get("DEV_DATABASE_USERNAME_"));
        String pwd = StringUtil.toStr(data.get("DEV_DATABASE_PASSWORD_"));
        String toUser = "usr_" + schoolCode;
        String cmd = "ssh root@172.16.5.54 /bin/bash /home/dmpshell/exp_imp.sh ";
        cmd = cmd + schoolCode + " " + username + " " + pwd + " " + toUser;
        int code = ShellUtil.execCmd(cmd);
        if (code != 0) {
            throw new BizException("导入导出数据库dmp文件失败");
        }
    }

}
